/*
 * com_wits_av_JniMediaStorage.cpp
 *
 *  Created on: 2018年8月28日
 *      Author: zhengboyuan
 */

#include "com_wits_av_JniMediaStorage.h"
#include <assert.h>
#include <stdio.h>
#include <string.h>

#include <android/native_window_jni.h>
#include <android/log.h>

#include "TCriticalSection.h"


#include "JniApplication.h"
#include "JniHelper.h"
#include <vector>

#include "CLog.h"


JavaVM* g_Jvm = NULL;

jint JNI_OnLoad(JavaVM *vm, void *reserved)
{
	__android_log_print(ANDROID_LOG_INFO, "jni", "--- JNI_OnLoad. JniMediaReader vm:%p", vm);

	g_Jvm = vm;

	JniApplication::instance().setJvm(vm);

	return JNI_VERSION_1_4;
}

void JNI_OnUnload(JavaVM *vm, void *reserved)
{

}


static bool getMFormat(JNIEnv * env, jclass thiz, jobject jfmt, MFormat& fmt)
{
	memset(&fmt, 0, sizeof(fmt));

	try
	{
		jni::Object obj(env, "com/wits/av/MFormat", jfmt);

		obj.getInt("codec", fmt.codec);
		obj.getInt("width", fmt.width);
		obj.getInt("height", fmt.height);
		obj.getInt("fps", fmt.framerate);
		obj.getInt("profile", fmt.profile);
		obj.getInt("clockRate", fmt.clockRate);

		obj.getInt("audioCodec", fmt.audioCodec);
		obj.getInt("audioProfile", fmt.audioProfile);
		obj.getInt("channels", fmt.channels);
		obj.getInt("sampleRate", fmt.sampleRate);
		obj.getInt("audioRate", fmt.audioRate);

		std::string props;
		if (obj.getByteArray("props", props) && props.size() > 0)
		{
			fmt.vProp = (uint8_t*)props.c_str();
			fmt.vPropSize = props.length();
		}
		else
		{
			std::string sps;
			std::string pps;
			std::string vps;
			obj.getByteArray("sps", sps);
			obj.getByteArray("pps", pps);
			obj.getByteArray("vps", vps);
			//todo
		}

		std::string audioConfig;
		if (obj.getByteArray("audioConfig", audioConfig) && audioConfig.size() > 0)
		{
			fmt.config = (uint8_t*)audioConfig.c_str();
			fmt.configSize = audioConfig.length();
		}

	}
	catch (...)
	{
		return false;
	}
	return true;
}


jobject createJObject(JNIEnv * env, const av::MRecord& record)
{
	jclass cls = env->FindClass("com/wits/av/MRecord");
	jmethodID constructor = env->GetMethodID(cls, "<init>", "()V");
	jobject object = env->NewObject(cls, constructor);
	return object;
}

///////////////////////////////////////////////////////////////////

av::MediaStorage& getStorage()
{
	return JniApplication::instance().getStorage();
}




///////////////////////////////////////////////////////////////////

/*
 * Class:     com_wits_av_JniMediaStorage
 * Method:    mstorage_init
 * Signature: ()I
 */
JNIEXPORT jint JNICALL Java_com_wits_av_JniMediaStorage_mstorage_1init
  (JNIEnv * env, jclass thiz)
{
	JniApplication::init();
	return 0;
}

/*
 * Class:     com_wits_av_JniMediaStorage
 * Method:    mstorage_quit
 * Signature: ()V
 */
JNIEXPORT void JNICALL Java_com_wits_av_JniMediaStorage_mstorage_1quit
  (JNIEnv * env, jclass thiz)
{
	JniApplication::instance().releaseListener(env);

	JniApplication::quit();
}

/*
 * Class:     com_wits_av_JniMediaStorage
 * Method:    mstorage_open
 * Signature: (Ljava/lang/String;)Z
 */
JNIEXPORT jboolean JNICALL Java_com_wits_av_JniMediaStorage_mstorage_1open
  (JNIEnv * env, jclass thiz, jstring jdbpath)
{
	jni::UtfString dbpath(env, jdbpath);

	return JniApplication::instance().open(dbpath.c_str());
}

/*
 * Class:     com_wits_av_JniMediaStorage
 * Method:    mstorage_close
 * Signature: ()V
 */
JNIEXPORT void JNICALL Java_com_wits_av_JniMediaStorage_mstorage_1close
  (JNIEnv * env, jclass thiz)
{
	JniApplication::instance().releaseListener(env);

	JniApplication::instance().close();
}

/*
 * Class:     com_wits_av_JniMediaStorage
 * Method:    mstorage_isOpen
 * Signature: ()Z
 */
JNIEXPORT jboolean JNICALL Java_com_wits_av_JniMediaStorage_mstorage_1isOpen
  (JNIEnv * env, jclass thiz)
{
	return JniApplication::instance().isOpen();
}

/*
 * Class:     com_wits_av_JniMediaStorage
 * Method:    mstorage_setRecordDir
 * Signature: (Ljava/lang/String;F)Z
 */
JNIEXPORT jboolean JNICALL Java_com_wits_av_JniMediaStorage_mstorage_1setRecordDir
  (JNIEnv * env, jclass thiz, jstring jdirpath, jfloat jpercent)
{
	jni::UtfString dirpath(env, jdirpath);
	return getStorage().setRecordDir(dirpath.c_str(), jpercent);
}

/*
 * Class:     com_wits_av_JniMediaStorage
 * Method:    mstorage_setFileDuration
 * Signature: (I)V
 */
JNIEXPORT void JNICALL Java_com_wits_av_JniMediaStorage_mstorage_1setFileDuration
  (JNIEnv * env, jclass thiz, jint jduration)
{
	if (jduration <= 0)
	{
		return;
	}

	getStorage().setFileDuration(jduration);
}

/*
 * Class:     com_wits_av_JniMediaStorage
 * Method:    mstorage_setFileFormat
 * Signature: (I)V
 */
JNIEXPORT void JNICALL Java_com_wits_av_JniMediaStorage_mstorage_1setFileFormat
  (JNIEnv * env, jclass thiz, jint jfmt)
{
	av::RecordFileFormat fileFormat = av::kPsFile;
	getStorage().setFileFormat(fileFormat);
}

/*
 * Class:     com_wits_av_JniMediaStorage
 * Method:    mstorage_setCoverageRecord
 * Signature: (I)V
 */
JNIEXPORT void JNICALL Java_com_wits_av_JniMediaStorage_mstorage_1setCoverageRecord
  (JNIEnv * env, jclass thiz, jint jcount)
{
	getStorage().setCoverageRecord(jcount);
}

/*
 * Class:     com_wits_av_JniMediaStorage
 * Method:    mstorage_setRecordNameTemplate
 * Signature: (Ljava/lang/String;)V
 */
JNIEXPORT void JNICALL Java_com_wits_av_JniMediaStorage_mstorage_1setRecordNameTemplate
  (JNIEnv * env, jclass thiz, jstring jtmpl)
{
	jni::UtfString tmpl(env, jtmpl);
	getStorage().setRecordNameTemplate(tmpl.c_str());
}

/*
 * Class:     com_wits_av_JniMediaStorage
 * Method:    mstorage_setEventCallback
 * Signature: (Lcom/wits/av/MediaStorageListener;)V
 */
JNIEXPORT void JNICALL Java_com_wits_av_JniMediaStorage_mstorage_1setEventCallback
  (JNIEnv * env, jclass thiz, jobject jlistener)
{
	JniApplication::instance().setListener(env, jlistener);
}


/*
 * Class:     com_wits_av_JniMediaStorage
 * Method:    mstorage_openChannel
 * Signature: (Ljava/lang/String;)I
 */
JNIEXPORT jint JNICALL Java_com_wits_av_JniMediaStorage_mstorage_1openChannel
  (JNIEnv * env, jclass thiz, jstring jdevice)
{
	jni::UtfString device(env, jdevice);
	return JniApplication::instance().openChannel(device.c_str());
}

/*
 * Class:     com_wits_av_JniMediaStorage
 * Method:    mstorage_closeChannel
 * Signature: (I)V
 */
JNIEXPORT void JNICALL Java_com_wits_av_JniMediaStorage_mstorage_1closeChannel
  (JNIEnv * env, jclass thiz, jint jhandle)
{
	JniApplication::instance().closeChannel(jhandle);
}

/*
 * Class:     com_wits_av_JniMediaStorage
 * Method:    mstorage_startRecord
 * Signature: (ILcom/wits/av/MFormat;)Z
 */
JNIEXPORT jboolean JNICALL Java_com_wits_av_JniMediaStorage_mstorage_1startRecord
  (JNIEnv * env, jclass thiz, jint jhandle, jobject jfmt)
{
	MediaStorageChannelPtr channel = JniApplication::instance().getChannel(jhandle);
	if (!channel)
	{
		return false;
	}

	MFormat fmt = MFormat();
	if (!getMFormat(env, thiz, jfmt, fmt))
	{
		return false;
	}

	return channel->startRecord(fmt);
}

/*
 * Class:     com_wits_av_JniMediaStorage
 * Method:    mstorage_stopRecord
 * Signature: (I)V
 */
JNIEXPORT void JNICALL Java_com_wits_av_JniMediaStorage_mstorage_1stopRecord
  (JNIEnv * env, jclass thiz, jint jhandle)
{
	MediaStorageChannelPtr channel = JniApplication::instance().getChannel(jhandle);
	if (!channel)
	{
		return ;
	}

	channel->stopRecord();
}

/*
 * Class:     com_wits_av_JniMediaStorage
 * Method:    mstorage_writePacket
 * Signature: (ILcom/wits/av/MPacket;)V
 */
JNIEXPORT void JNICALL Java_com_wits_av_JniMediaStorage_mstorage_1writePacket
  (JNIEnv * env, jclass thiz, jint jhandle, jobject jpkt)
{
	MediaStorageChannelPtr channel = JniApplication::instance().getChannel(jhandle);
	if (!channel)
	{
		return ;
	}

	typedef std::shared_ptr< jni::ByteArray >	JByteArrayPtr;

	MPacket pkt;
	memset(&pkt, 0, sizeof(pkt));

	jni::Object obj(env, "com/wits/av/MPacket", jpkt);
	obj.getInt("type", pkt.type);
	obj.getInt("duration", pkt.duration);
	obj.getInt("flags", pkt.flags);
	obj.getLong("pts", pkt.pts);

	jbyteArray jdata;
	obj.getByteArray("data", jdata);
	jni::ByteArray byteArray(env, jdata);

	pkt.data = (uint8_t*)byteArray.element();
	pkt.size = byteArray.length();

	if (pkt.type == MTYPE_AUDIO)
	{
		channel->writeAudio(pkt.data, pkt.size, pkt.pts);
	}
	else
	{
		channel->writeVideo(pkt.data, pkt.size, pkt.pts);
	}
}

/*
 * Class:     com_wits_av_JniMediaStorage
 * Method:    mstorage_setRecordType
 * Signature: (II)V
 */
JNIEXPORT void JNICALL Java_com_wits_av_JniMediaStorage_mstorage_1setRecordType
  (JNIEnv * env, jclass thiz, jint jhandle, jint jtype)
{
	MediaStorageChannelPtr channel = JniApplication::instance().getChannel(jhandle);
	if (!channel)
	{
		return ;
	}

	channel->setRecordType(jtype);
}

/*
 * Class:     com_wits_av_JniMediaStorage
 * Method:    mstorage_setRecorder
 * Signature: (ILjava/lang/String;)V
 */
JNIEXPORT void JNICALL Java_com_wits_av_JniMediaStorage_mstorage_1setRecorder
  (JNIEnv * env, jclass thiz, jint jhandle, jstring jrecorder)
{
	MediaStorageChannelPtr channel = JniApplication::instance().getChannel(jhandle);
	if (!channel)
	{
		return ;
	}

	jni::UtfString recorder(env, jrecorder);
	channel->setRecorder(recorder.c_str());
}



/*
 * Class:     com_wits_av_JniMediaStorage
 * Method:    mstorage_countRecord
 * Signature: (Lcom/wits/av/MRecordQuery;)I
 */
JNIEXPORT jint JNICALL Java_com_wits_av_JniMediaStorage_mstorage_1countRecord
  (JNIEnv * env, jclass thiz, jobject jquery)
{
	av::MRecordQuery query = av::MRecordQuery();

	jni::Object obj(env, "com/wits/av/MRecordQuery", jquery);
	obj.getInt("recordType", query.recordType);
	obj.getLong("fromTime", (jlong&)query.fromTime);
	obj.getLong("toTime", (jlong&)query.toTime);

	std::string device;
	obj.getString("device", device);
	std::string recorder;
	obj.getString("recorder", recorder);

	query.device = device.c_str();
	query.recorder = recorder.c_str();

	return getStorage().countRecord(query);
}

/*
 * Class:     com_wits_av_JniMediaStorage
 * Method:    mstorage_findRecord
 * Signature: (Lcom/wits/av/MRecordQuery;I[Lcom/wits/av/MRecord;)I
 */
JNIEXPORT jint JNICALL Java_com_wits_av_JniMediaStorage_mstorage_1findRecord
  (JNIEnv * env, jclass thiz, jobject jquery, jint joffset, jobjectArray jrecords)
{
	if (!jrecords)
	{
		return 0;
	}

	int length = env->GetArrayLength(jrecords);
	if (length <= 0)
	{
		return 0;
	}

	av::MRecordQuery query = av::MRecordQuery();

	jni::Object obj(env, "com/wits/av/MRecordQuery", jquery);
	obj.getInt("recordType", query.recordType);
	obj.getLong("fromTime", (jlong&)query.fromTime);
	obj.getLong("toTime", (jlong&)query.toTime);

	std::string device;
	obj.getString("device", device);
	std::string recorder;
	obj.getString("recorder", recorder);

	query.device = device.c_str();
	query.recorder = recorder.c_str();

	std::vector< av::MRecord > records;
	records.resize(128);

	CLog::info("findRecord. device: %s\n", device.c_str());
	int count = getStorage().findRecord(query, joffset, &(records[0]), length);

	for (int i = 0; i < count; ++ i)
	{
		av::MRecord& rec = records[i];
		jobject jrecord = createJObject(env, records[i]);

		jni::Object record(env, "com/wits/av/MRecord", jrecord);
		record.setInt("id", rec.id);
		record.setString("device", rec.device);
		record.setLong("startTime", rec.startTime);
		record.setLong("stopTime", rec.stopTime);
		record.setLong("filesize", rec.filesize);
		record.setInt("events", rec.events);
		record.setInt("type", rec.type);
		record.setInt("duration", rec.duration);
		record.setString("filename", rec.filename);
		record.setString("filepath", rec.filepath);
		record.setString("url", rec.url);
		record.setString("recorder", rec.recorder);
		record.setString("memo", rec.memo);

		env->SetObjectArrayElement(jrecords, i, jrecord);

		env->DeleteLocalRef(jrecord);
	}

	CLog::info("findRecord. return %d\n", count);

	return count;
}

/*
 * Class:     com_wits_av_JniMediaStorage
 * Method:    mstorage_deleteRecord
 * Signature: (I)Z
 */
JNIEXPORT jboolean JNICALL Java_com_wits_av_JniMediaStorage_mstorage_1deleteRecord
  (JNIEnv * env, jclass thiz, jint jid)
{
	return getStorage().deleteRecord(jid);
}

/*
 * Class:     com_wits_av_JniMediaStorage
 * Method:    mstorage_clearBrokenRecord
 * Signature: (Ljava/lang/String;)V
 */
JNIEXPORT void JNICALL Java_com_wits_av_JniMediaStorage_mstorage_1clearBrokenRecord
  (JNIEnv * env, jclass thiz, jstring jdevice)
{
	jni::UtfString device(env, jdevice);
	getStorage().clearBrokenRecord(device.c_str());
}


